HAL SD typical use cases for migration

This section provides information about HAL SD use cases for STM32 HAL1 to HAL2 migration.

Note

The typical read and write operations—whether using polling, DMA, or interrupt (IT) modes—remain largely unchanged for the user between HAL1 and HAL2. The main difference in HAL2 is the refactored callback registration and handling model: transfer completion callbacks are now unified and event-driven, simplifying application code.

The most significant changes in HAL2 are in linked-list DMA management. Linked-list operations now use a generic HAL Q API for node and queue management. HAL Q is a new HAL layer introduced in HAL2 that provides advanced linked-list features shared across multiple HAL drivers. It centralizes linked-list management, improving code reuse and maintainability.

Card removal/insertion handling differs from HAL1 to HAL2:

  • With HAL1, in the event of card plug-in/plug-off, the user should call HAL_SD_InitCard() upon the card insertion.

  • With HAL2 the application calls HAL_SD_NotifyCardRemoval() on removal to reset the peripheral to default configuration and HAL_SD_NotifyCardInsertion() on insertion to initialize the card and apply user configuration.

HAL SD polling transfer mode

This transfer sequence in polling mode remains unchanged between HAL1 and HAL2.

@startuml
hide footbox
participant "User Application" as part1
participant "HAL SD" as part2
activate part1
activate part2
group I/O Operation process
part1->part2 : HAL_SD_Erase()
part2 --> part1: HAL Status
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
part1->part2 : HAL_SD_WriteBlocks()
part2 --> part1: HAL Status
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
part1->part2 : HAL_SD_ReadBlocks()
part2 --> part1: HAL Status
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
end
@enduml

uint8_t Buffer_Block_Tx[BufferWordsSize], Buffer_Block_Rx[BufferWordsSize];
/* Write one block then read back (polling) */

SD_state = HAL_SD_Erase(&hsd_card, BlockStartAdd, BlockEndAdd);
/* Wait for the card transfer status */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}

if (SD_state == HAL_OK)
{
   /* Write NumberOfBlocks blocks of BufferWordsSize bytes on address BlockAddress */
   SD_state = HAL_SD_WriteBlocks(&hsd_card, Buffer_Block_Tx, BlockAddress, NumberOfBlocks, UserTimeout_ms);
}
else
{
   Error_Handler();
}

/* Wait for the card transfer status before starting a read operation */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}

if (SD_state == HAL_OK)
{
   /* Read NumberOfBlocks blocks of BufferWordsSize bytes from address BlockAddress */
   SD_state = HAL_SD_ReadBlocks(&hsd_card, Buffer_Block_Rx, BlockAddress, NumberOfBlocks, UserTimeout_ms);
}
else
{
   Error_Handler();
}

/* Wait for the card transfer status */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}

HAL SD IT transfer mode

HAL1

HAL2

IT transfer mode uses HAL_SD_WriteBlocks_IT and HAL_SD_ReadBlocks_IT for non-blocking operations. Transfer complete is signaled via callback functions (registered with HAL_SD_RegisterCallback for TX and RX complete separately).

IT transfer sequence remains mostly unchanged between HAL1 and HAL2, but callback registration is unified via HAL_SD_RegisterXferCpltCallback registering a single callback that handles both TX and RX events through an event parameter.

@startuml
hide footbox
participant "User Application" as part1
participant "HAL SD" as part2
participant "NVIC" as part3
activate part1
activate part2

group Register transfer complete callbacks
part1->part2 : HAL_SD_RegisterCallback(&hsd_card, HAL_SD_TX_CPLT_CB_ID, TxCpltCallback)
part2 --> part1
part1->part2 : HAL_SD_RegisterCallback(&hsd_card, HAL_SD_RX_CPLT_CB_ID, RxCpltCallback)
part2 --> part1
end

group I/O Operation process
part1->part2 : HAL_SD_WriteBlocks_IT()
part2-->part1 : HAL Status
part3 -> part2 : HAL_SD_IRQHandler()
part2 -> part1 : Notify the User application with Transmission complete
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
part1->part2 : HAL_SD_ReadBlocks_IT()
part2-->part1 : HAL Status
part3 -> part2 : HAL_SD_IRQHandler()
part2 -> part1 : Notify the User application with Reception complete
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
end
@enduml

Code Snippet (IT)

HAL1

HAL2

uint8_t Buffer_Block_Tx[BufferWordsSize];
uint8_t Buffer_Block_Rx[BufferWordsSize];

/* Register the Tx and Rx
   Transfer complete callbacks */
HAL_SD_RegisterCallback(&hsd_card,
                         HAL_SD_TX_CPLT_CB_ID,
                         TxCpltCallback);
HAL_SD_RegisterCallback(&hsd_card,
                         HAL_SD_RX_CPLT_CB_ID,
                         RxCpltCallback);

/* Write NumberOfBlocks blocks of BufferWordsSize
   bytes on address BlockAddress */
SD_state = HAL_SD_WriteBlocks_IT(&hsd_card,
                                   Buffer_Block_Tx,
                                   BlockAddress,
                                   NumberOfBlocks);
/* Wait for the Writing process to complete */
while(sdtransfertx == 0)
{}

/* Wait for the card transfer status before
   starting a read operation */
   while(HAL_SD_GetCardState(&hsd_card)
      != HAL_SD_CARD_TRANSFER)
{}

if (SD_state == HAL_OK)
{
   /* Read NumberOfBlocks blocks of BufferWordsSize
      bytes from address BlockAddress */
   SD_state = HAL_SD_ReadBlocks_IT(&hsd_card,
                                     Buffer_Block_Rx,
                                     BlockAddress,
                                     NumberOfBlocks);
}
else
{
   Error_Handler();
}
/* Wait for the Reading process is completed */
while(sdtransferrx == 0)
{}
/* Wait for the card transfer status before
   proceeding with another SD operation */
   while(HAL_SD_GetCardState(&hsd_card)
      != HAL_SD_CARD_TRANSFER)
{}

void TxCpltCallback(SD_HandleTypeDef *hsd) {
   sdtransfertx = 1;
}

void RxCpltCallback(SD_HandleTypeDef *hsd) {
   sdtransferrx = 1;
}
 uint8_t Buffer_Block_Tx[BufferWordsSize];
 uint8_t Buffer_Block_Rx[BufferWordsSize];

 /* Register the Tx and Rx Transfer complete callbacks */
 HAL_SD_RegisterXferCpltCallback(&hsd, MySdXferCallback);







 /* Write NumberOfBlocks blocks of
    BufferWordsSize bytes on address BlockAddress */
 SD_state = HAL_SD_WriteBlocks_IT(&hsd_card,
                                    Buffer_Block_Tx,
                                    BlockAddress,
                                    NumberOfBlocks);
 /* Wait for the Writing process to complete */
 while(sdtransfertx == 0)
 {}

 /* Wait for the card transfer status
    before starting a read operation */
    while(HAL_SD_GetCardState(&hsd_card)
       != HAL_SD_CARD_TRANSFER)
 {}

 if (SD_state == HAL_OK)
 {
    /* Read NumberOfBlocks blocks of
       BufferWordsSize bytes from address BlockAddress */
    SD_state = HAL_SD_ReadBlocks_IT(&hsd_card,
                                      Buffer_Block_Rx,
                                      BlockAddress,
                                      NumberOfBlocks);
 }
 else
 {
    Error_Handler();
 }
 /* Wait for the Reading process is completed */
 while(sdtransferrx == 0)
 {}
 /* Wait for the card transfer status
 before proceeding with another SD operation */
    while(HAL_SD_GetCardState(&hsd_card)
       != HAL_SD_CARD_TRANSFER)
 {}

 void MySdXferCallback(hal_sd_handle_t *hsd,
                       hal_sd_event_cb_t cb_event) {
    switch (cb_event) {
           case HAL_SD_EVENT_CB_TX_CPLT:
                 sdtransfertx = 1;
                 break;
           case HAL_SD_EVENT_CB_RX_CPLT:
                 sdtransferrx = 1;
                 break;
           default: break;
     }
}

HAL SD DMA transfer mode

HAL1

HAL2

DMA transfer mode uses HAL_SD_WriteBlocks_DMA and HAL_SD_ReadBlocks_DMA for non-blocking operations. Transfer complete is signaled via callback functions (registered with HAL_SD_RegisterCallback for TX and RX complete separately).

DMA transfer sequence remains mostly unchanged between HAL1 and HAL2, but callback registration is unified via HAL_SD_RegisterXferCpltCallback registering a single callback that handles both TX and RX events through an event parameter.

@startuml
hide footbox
participant "User Application" as part1
participant "HAL SD" as part2
participant "NVIC" as part3
activate part1
activate part2
group I/O Operation process
part1->part2 : HAL_SD_WriteBlocks_DMA()
part2-->part1 : HAL Status
part3 -> part2 : HAL_SD_IRQHandler()
part2 -> part1 : Notify the User application with Transmission complete
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
part1->part2 : HAL_SD_ReadBlocks_DMA()
part2-->part1 : HAL Status
part3 -> part2 : HAL_SD_IRQHandler()
part2 -> part1 : Notify the User application with Reception complete
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
end
@enduml

Code Snippet (DMA)

uint8_t Buffer_Block_Tx[BufferWordsSize], Buffer_Block_Rx[BufferWordsSize];
/* Write NumberOfBlocks blocks of BufferWordsSize bytes on address BlockAddress */
SD_state = HAL_SD_WriteBlocks_DMA(&hsd_card, Buffer_Block_Tx, BlockAddress, NumberOfBlocks);
  /* Wait for the Writing process to complete */
while(sdtransfertx == 0) /* The variable ``sdtransfertx`` is updated by
                             the callback function */
{}

/* Wait for the card transfer status before starting a read operation */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}

if (SD_state == HAL_OK)
{
   /* Read NumberOfBlocks blocks of BufferWordsSize bytes from address BlockAddress */
   SD_state = HAL_SD_ReadBlocks_DMA(&hsd_card, Buffer_Block_Rx, BlockAddress, NumberOfBlocks);
}
else
{
   Error_Handler();
}
/* Wait for the Reading process is completed */
while(sdtransferrx == 0) /* The variable ``sdtransferrx`` is updated by
                             the callback function  */
{}
/* Wait for the card transfer status before proceeding with another SD operation */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}

HAL SD linked-list DMA transfer

HAL1

HAL2

Linked-list DMA transfer mode uses linked list node building and insertion APIs ( HAL_SDEx_DMALinkedList_BuildNode(), HAL_SDEx_DMALinkedList_InsertNode()) to create a queue of DMA transfer nodes. The user initiates write and read operations using linked-list specific functions ( HAL_SDEx_DMALinkedList_WriteBlocks(), HAL_SDEx_DMALinkedList_ReadBlocks()).

In HAL2 linked-list DMA transfer is updated with most of linked-list management handled by HAL Q driver (a new driver provided with HAL2). The user builds nodes using HAL_SD_FillNodeConfig(), initializes the queue with HAL_Q_Init(), and inserts nodes using HAL_Q_InsertNode(). The I/O operations are performed with HAL_SD_WriteBlocks_LinkedList() and HAL_SD_ReadBlocks_LinkedList(). Refer to linked-list management I/O operations and HAL Q documentation for more details.

@startuml
hide footbox
participant "User Application" as part1
participant "HAL SD" as part2
participant "NVIC" as part3

activate part1
activate part2

group Linked List Building
   part1->part2 : HAL_SDEx_DMALinkedList_BuildNode(&SD_Nx, &SD_Node_Conf)
   note right : Build node SD_Nx from SD_Node_Conf filled by user. \nRepeat for each node to be configured.
   part1->part2 : HAL_SDEx_DMALinkedList_InsertNode(&Q1, <previous_node>, &SD_Nx)
   note right : Insert each node into the queue as needed. \nRepeat for all nodes to be linked.
end

group I/O Operation process
part1->part2 : HAL_SD_Erase()
part2 --> part1: Erase completed

part1->part2 : HAL_SDEx_DMALinkedList_WriteBlocks()
part2-->part1

part3 -> part2 : HAL_SD_IRQ_HANDLER()
part2 -> part1 : Notify TX complete
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER

part1->part2 : HAL_SDEx_DMALinkedList_ReadBlocks()
part3 -> part2 : HAL_SD_IRQ_HANDLER()
part2 -> part1 : Notify RX complete
part1->part1 : Wait for HAL_SD_GetCardState() == HAL_SD_CARD_TRANSFER
end
@enduml

Code Snippet (Linked-list DMA)

HAL1

HAL2

/****************************************************** Build SDMMC node 1 */
 SD_Node_Conf.BufferAddress = (uint32_t) &(Buffer_Tx[0]);
 SD_Node_Conf.BufferSize = BLOCKSIZE*1;
 HAL_SDEx_DMALinkedList_BuildNode(&SD_N1, &SD_Node_Conf);
/************************************************************ Build node 2 */
 SD_Node_Conf.BufferAddress = (uint32_t) &(Buffer_Tx[BufferWordsSize]);
 SD_Node_Conf.BufferSize = BLOCKSIZE*2;
 HAL_SDEx_DMALinkedList_BuildNode(&SD_N2, &SD_Node_Conf);
/************************************************************ Build node 3 */
 SD_Node_Conf.BufferAddress = (uint32_t) &(Buffer_Tx[BufferWordsSize*3]);
 SD_Node_Conf.BufferSize = BLOCKSIZE*3;
 HAL_SDEx_DMALinkedList_BuildNode(&SD_N3, &SD_Node_Conf);

/* Define list object */
 SD_DMALinkedListTypeDef Q_write;


/*********************************************** Connect node 1 to Queue 1 */
 HAL_SDEx_DMALinkedList_InsertNode(&Q_write, NULL, &SD_N1);
/*********************************************** Connect node 2 to Queue 1 */
 HAL_SDEx_DMALinkedList_InsertNode(&Q_write, &SD_N1, &SD_N2);
/*********************************************** Connect node 3 to Queue 1 */
 HAL_SDEx_DMALinkedList_InsertNode(&Q_write, &SD_N2, &SD_N3);

 /*  Write steps */
 /* Write blocks using DMA transfer with linked list */
 if (HAL_SDEx_DMALinkedList_WriteBlocks(&hsd_card, &Q_write, 1000, 12) != HAL_OK)
 {
   Error_Handler();
 }

 /*************************************************** Wait for TX complete */
 while (sdtransfertx == 0)
 {}
 /******************************************* Wait for card transfer state */
 while (HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
 {}


/* Read steps */
 SD_Node_Conf.BufferAddress = (uint32_t) &(Buffer_Rx[0]);
 SD_Node_Conf.BufferSize = BLOCKSIZE;
 HAL_SDEx_DMALinkedList_BuildNode(&SD_N4, &SD_Node_Conf);

 SD_Node_Conf.BufferAddress = (uint32_t) &(Buffer_Rx[BufferWordsSize*2]);
 SD_Node_Conf.BufferSize = BLOCKSIZE*2;
 HAL_SDEx_DMALinkedList_BuildNode(&SD_N5, &SD_Node_Conf);

 SD_Node_Conf.BufferAddress = (uint32_t) &(Buffer_Rx[BufferWordsSize*3]);
 SD_Node_Conf.BufferSize = BLOCKSIZE*3;
 HAL_SDEx_DMALinkedList_BuildNode(&SD_N6, &SD_Node_Conf);

 /* Define list object */
 SD_DMALinkedListTypeDef Q_read;


 HAL_SDEx_DMALinkedList_InsertNode(&Q_read, NULL, &SD_N4);
 HAL_SDEx_DMALinkedList_InsertNode(&Q_read, &SD_N4, &SD_N5);
 HAL_SDEx_DMALinkedList_InsertNode(&Q_read, &SD_N5, &SD_N6);

 /* Read back written block using DMA linked-list transfer */
 if (HAL_SDEx_DMALinkedList_ReadBlocks(&hsd_card, &Q_read, 1000,12) != HAL_OK)
 {
   Error_Handler();
 }
 /*********************************************** Wait for RX/TX complete */
 while (sdtransferrx == 0)
 {}
 /****************************************** Wait for card transfer state */
 while (HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
 {}
/**************************************************** Build SDMMC node 1 */


HAL_SD_FillNodeConfig(&SD_N1, &(Buffer_Tx[0]), 1 * BLOCKSIZE);
/********************************************************** Build node 2 */


HAL_SD_FillNodeConfig(&SD_N2, &(Buffer_Tx[BufferWordsSize]), 2 * BLOCKSIZE);
/********************************************************** Build node 3 */


HAL_SD_FillNodeConfig(&SD_N3, &(Buffer_Tx[BufferWordsSize * 3]), 3 * BLOCKSIZE);

/************************************************** HAL Q initialization */
hal_q_t Q_write;
HAL_Q_Init(&Q_write, &HAL_SD_DescOps);

/********************************************* Connect node 1 to Queue 1 */
HAL_Q_InsertNode(&Q_write, NULL, &SD_N1);
/********************************************* Connect node 2 to Queue 1 */
HAL_Q_InsertNode(&Q_write, &SD_N1, &SD_N2);
/********************************************* Connect node 3 to Queue 1 */
HAL_Q_InsertNode(&Q_write, &SD_N2, &SD_N3);

/*  Write steps */
/* Write blocks using DMA transfer with linked list */
if ((SD_state = HAL_SD_WriteBlocks_LinkedList(&hsd_card, 1000, 12, &Q_write)) != HAL_OK)
{
  Error_Handler();
}

/************************************************** Wait for TX complete */
while(sdtransfertx == 0)
{}
/************************************* Wait for the card transfer status */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}


/* Read steps */


HAL_SD_FillNodeConfig(&SD_N4, &(Buffer_Rx[0]), 1 * BLOCKSIZE);



HAL_SD_FillNodeConfig(&SD_N5, &(Buffer_Rx[BufferWordsSize*2]), 2 * BLOCKSIZE);



HAL_SD_FillNodeConfig(&SD_N6, &(Buffer_Rx[BufferWordsSize*3]), 3 * BLOCKSIZE);

/************************************************** HAL Q initialization */
hal_q_t Q_read;
HAL_Q_Init(&Q_read, &HAL_SD_DescOps);

HAL_Q_InsertNode(&Q_read, NULL, &SD_N4);
HAL_Q_InsertNode(&Q_read, &SD_N4, &SD_N5);
HAL_Q_InsertNode(&Q_read, &SD_N5, &SD_N6);

/* Read back written block using DMA linked-list transfer */
if (HAL_SD_ReadBlocks_LinkedList(&hsd_card, 1000, 12, &Q_read) != HAL_OK)
{
  Error_Handler();
}
/*********************************************** Wait for RX/TX complete */
while ((sdtransferrx == 0))
{}
/****************************************** Wait for card transfer state */
while(HAL_SD_GetCardState(&hsd_card) != HAL_SD_CARD_TRANSFER)
{}

HAL SD card removal/insertion handling

Card presence is typically monitored using a dedicated Card Detect (CD) GPIO pin, with EXTI interrupts used to notify the application of insertion or removal events.

HAL1

HAL2

In HAL1, upon card removal (detected via EXTI), if there is any ongoing processes (read/write) the application should stop them (for instance stop transfers with HAL_SD_Abort() or HAL_SD_Abort_IT()). Upon insertion, the application calls HAL_SD_InitCard() which first applies default configurations to the SDMMC peripheral, initializes the card, and then applies user configurations stored in init struct passed within the handle hsd_card.

In HAL2, the application similarly handles EXTI notifications. On removal, it aborts ongoing processes in the same way but also calls HAL_SD_NotifyCardRemoval() to apply default configurations. On insertion, it calls HAL_SD_NotifyCardInsertion() to re-apply user configuration provided within user_config parameter.

@startuml
hide footbox
participant "User Application" as app
participant "HAL SD" as hal
participant "NVIC" as nvic
activate app

group Normal operation
alt Card is Present
   app -> hal : Periodic operation (read/write)
end
deactivate app
end

nvic -> app : EXTI IRQ (CD removed/inserted)
activate app
group Hot-Plug Check
    app -> app : Read CD Pin

    alt   Card has been removed
          app -> app : Update Card presence Status: DISCONNECTED
          app -> hal : Stop ongoing SD processes

    else  Card has been inserted
          app -> hal : HAL_SD_InitCard()
          activate hal
          hal --> app
          deactivate hal
          app -> app : Update Card presence Status: CONNECTED
    end
deactivate app
end
@enduml

Code Snippet (card removal/insertion)

HAL1

HAL2

volatile uint8_t card_present = 0;

/* Example guarded I/O */
void USER_SD_Application_routine()
{
  if (card_present)
  {
  /* Perform read/write only if card is present */
  /* ... */
  }
}

/* EXTI callback hooked to the SD card-detect GPIO pin */
void USER_SD_HotPlugCallback()
{
  if (USER_SD_READ_DETECT_PIN())
  {
    /* Card removed */
    card_present = 0U;
    /* Abort ongoing processes*/
    User_SD_StopOngoingProcess(&hsd_card);


  }
  else
  {
    /* Card inserted */
    HAL_SD_InitCard(&hsd_card);
    card_present = 1U;
  }
}
volatile uint8_t card_present = 0;

/* Example guarded I/O */
void USER_SD_Application_routine()
{
  if (card_present)
  {
  /* Perform read/write only if card is present */
  /* ... */
  }
}

/* EXTI callback hooked to the SD card-detect GPIO pin */
void USER_SD_HotPlugCallback()
{
  if (USER_SD_READ_DETECT_PIN())
  {
    /* Card removed */
    card_present = 0U;
    /* Abort ongoing processes*/
    USER_SD_StopOngoingProcess(&hsd_card);

    HAL_SD_NotifyCardRemoval(&hsd_card);
  }
  else
  {
    /* Card inserted */
    HAL_SD_NotifyCardInsertion(&hsd_card, &user_config);
    card_present = 1U;
  }
}